const fs = require('fs');
const spawn = require('child_process').spawn;

/**
 *   ___________   ___________   ___________
 *  | 被检查者 |   | 检查者a |   | 检查者b  |
 *  | main.js |   | MyWatch |   | MyWatch |
 *  -----------    ---------   -----------
 *
 *  当 main.js 被执行后(node main.js) 会不停的写一个文件 _watch/main.log (频率是 20 秒写一次)
 *  同时 MyWatch（检查者a 和 检查者b）同和 main.js 同时启动，并每 30 秒检查一次， _watch/main.log 是否异常
 *  同时 检查者a 和 检查者b 也相互检查是否异常
 *
 *  main.js 写文件相当于告知 检查者a 和 检查者b ，他（main.js） 依旧正常运行中
 *  而 30 秒的只是一个时间节点，如果30秒时未收到 main.js 的修改记录，表示 main.js 可能挂了，需要重新启动
 *
 *  与之一致， 检查者a 和 检查者b 相互也是需要告知对方自己还活着，原理一致
 *  检查者a 通过不停的写 文件 _watch/watcherA.log 告知 检查者b 自己活着
 *  检查者b 同理
 */

class MyWatch {
    /**
     * @param fileName          当前代码文件名（检查者a 要去）
     * @param checkFileObjs     被检查的文件（一个数组，包含文件名和失败处理方法）
     * @param checkTime         多久检查一次
     * @param writeCheckTime    多久写一次文件（相当于告知其他程序该代码没有执行异常）
     */
    constructor(fileName,checkFileObjs,checkTime,writeCheckTime) {
        this.fileName = fileName;
        this.checkTime = checkTime || 30 *  1000;
        this.writeCheckTime = writeCheckTime || 20 * 1000;
        this.fileNameToIndex = {};
        this.checkFileObjs = checkFileObjs.map((_,index) => {
            this.fileNameToIndex[_.fileName] = index;
            return Object.assign(_,{
                // fileName: '',
                // command: ''
                live: false,
                index
            });
        });
        this.init();
    }

    init() {
        let $this = this;
        fs.watch('./',{},(ev,fn) => {
            if (fn in $this.fileNameToIndex) {
                $this.checkFileObjs[$this.fileNameToIndex[fn]].live = true;
            }
        });
        // 检查谁挂了
        setTimeout(this.checkFileLive.bind(this),this.checkTime);
        // 写，给其他人检查
        setInterval(_ => {
            fs.writeFileSync($this.fileName,(new Date().getTime()))
        },this.writeCheckTime);
    }

    /**
     * 按规定时间一直执行
     */
    checkFileLive() {
        let $this = this;
        this.checkFileObjs.forEach(_ => {
            console.log(`[current fileName] : ${$this.fileName} and [fileName] : ${_.fileName} and [is live] : ${_.live}`);
            if (_.live) {
                $this.checkFileObjs[_.index].live = false;
            } else {
                this.ifFailToRun(_.command);
            }
        });
        setTimeout(this.checkFileLive.bind(this),this.checkTime);
    }

    /**
     * 服务挂了之后启动代码
     * */
    ifFailToRun(command) {
        if (command) {
            spawn('start cmd /k ' + command,{
                shell: true
            });
        } else {
            console.log("fail");
        }
    }
}

module.exports =  MyWatch;